/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2015-2018. All rights reserved.
 * Description: Signature Verify CBB Library
 * Author: tangzonglei
 * Create: 2015
 * History: 2018/11/23 yebin code rule fixes
 */
#include "cmscbb_err_def.h"
#include "cmscbb_sdk.h"
#include "../cmscbb_common/cmscbb_common.h"
#include "../asn1/cmscbb_asn1_utils.h"
#include "../asn1/cmscbb_asn1_decode.h"
#include "../cmscbb_common/cmscbb_def.h"
#include "../x509/cmscbb_verify_info.h"

#if CMSCBB_WITHOUT_SECUREC == 0
#include "securec.h"
#endif

CMSCBB_ERROR_CODE CmscbbCreateVerifyInfo(CmscbbKeyAndAlgInfo **info)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbKeyAndAlgInfo *verifyInfo = NULL;
    CmscbbBigInt *p = NULL;
    ret = CmscbbMalloc((void**)&verifyInfo, sizeof(CmscbbKeyAndAlgInfo));
    if (CVB_FAILED(ret)) {
        CVB_LOG_ERROR(ret, CVB_NULL);
        return ret;
    }
    verifyInfo->hashAlg = AOID_UNKNOWN;
    verifyInfo->encAlg = AOID_UNKNOWN;
    verifyInfo->mgfAlg = AOID_UNKNOWN;
    verifyInfo->saltLen = 20; /* 20 is defaultValue, see RFC3477, section-8.1 */
    verifyInfo->trailerField = 1;
    verifyInfo->curveId = 0;
    ret = CmscbbMalloc((void**)&p, sizeof(CmscbbBigInt) * 3); /* 3 is the number of structures that require memory. */
    if (CVB_FAILED(ret)) {
        CmscbbFreeWithNullCheck(verifyInfo);
        CVB_LOG_ERROR(ret, CVB_NULL);
        return ret;
    }
    verifyInfo->n = p;
    verifyInfo->e = p + 1;
    verifyInfo->eccKey = p + 2; /* 2: offset 2 */
    *info = verifyInfo;
    return ret;
}

CMSCBB_ERROR_CODE CmscbbVerifyInfoInit(const CmscbbX509PubkeyInfo* pubKey, CmscbbAoids encAlgo,
    const CmscbbX509AlgoPssIdentifier* alg, CmscbbKeyAndAlgInfo* verifyInfo)
{
    CmscbbAoids mgf1Algo;
    CmscbbAoids curveId;
    CVB_BOOL legalFlag = (alg == NULL) || (verifyInfo == NULL);
    if (legalFlag) {
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }
    verifyInfo->encAlg = encAlgo;
    if (encAlgo == AOID_RSASSAPSS) {
        mgf1Algo = CmscbbGetHashAoidFromSign(&(alg->pssPara.maskGenAlgorithm.maskAlgo.algorithm));
        if (mgf1Algo == AOID_UNKNOWN) {
            CVB_LOG_ERROR(CMSCBB_ERR_PKI_CMS_HASH_ALGO, CVB_NULL);
            return (CMSCBB_ERR_PKI_CMS_HASH_ALGO);
        }
        verifyInfo->mgfAlg = mgf1Algo;
        /* 20 is  default Salt Value, see RFC8017, section-8.1, A.2.3 RSASSA-PSS */
        verifyInfo->saltLen = (alg->pssPara.saltLength == 0) ? 20 : alg->pssPara.saltLength;
        verifyInfo->trailerField = (alg->pssPara.trailerField == 0) ? 1 : alg->pssPara.trailerField;
        return CVB_SUCCESS;
    } else if (encAlgo == AOID_ECDSA) {
        curveId = CmscbbFindAoid(&(pubKey->algorithm.para));
        if (curveId == AOID_UNKNOWN) {
            CVB_LOG_ERROR(CMSCBB_ERR_PKI_CMS_CURID, CVB_NULL);
            return CMSCBB_ERR_PKI_CMS_CURID;
        }
        verifyInfo->curveId = curveId;
        return CVB_SUCCESS;
    }

    verifyInfo->mgfAlg = AOID_UNKNOWN;
    verifyInfo->saltLen = -1;
    verifyInfo->trailerField = -1;

    return CVB_SUCCESS;
}

/*
 * Prototype    : InternalGetBigIntFromOcts
 * Description  : get big int from  asn object
 * Params
 *   [OUT] e: a big int get from asn big int
 *   [IN] asnBigint: asn object stored big int
 * Return Value : CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/10 19:06  tangzonglei  Create
 */
#if CMSCBB_ENABLE_LOG
#define LENGTH_EXPONENT 3
#endif
CVB_STATIC CMSCBB_ERROR_CODE InternalGetBigIntFromOcts(CmscbbBigInt *e, const CmscbbAsnBigint *asnBigint)
{
    CMSCBB_ERROR_CODE ret;

    if (asnBigint->len > CMSCBB_MAX_INT_DIGITS) {
        CVB_LOG_ERROR1(CMSCBB_ERR_UNDEFINED, "Unsupported key length %d", asnBigint->len << LENGTH_EXPONENT);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    e->uiLength = asnBigint->len;
    if (asnBigint->len > CMSCBB_MAX_INT_DIGITS) {
        CVB_LOG_ERROR(CMSCBB_ERR_PKI_CERT_INVALID_CONTENT, CVB_NULL);
        return CMSCBB_ERR_PKI_CERT_INVALID_CONTENT;
    }
    ret = (CMSCBB_ERROR_CODE)memcpy_s(e->aVal, CMSCBB_MAX_INT_DIGITS, asnBigint->octs, asnBigint->len);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

    return CVB_SUCCESS;
}

static CMSCBB_ERROR_CODE InternalGetEccSignature(const CmscbbAsnOcts* sigInfo, CmscbbKeyAndAlgInfo* verifyInfo)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbEccSign* eccValue = NULL;
    CVB_UINT32 decodeLen;

    ret = CmscbbAsnDecode(sigInfo->octs, sigInfo->len, CmscbbGetCmscbbEccSign(), CVB_ASN_NORMAL,
        (CVB_VOID**)&eccValue, &decodeLen);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, "decode Ecc Signature Fialed!");
        return ret;
    }

    ret = InternalGetBigIntFromOcts(verifyInfo->n, &(eccValue->xValue));
    if (CVB_FAILED(ret)) {
        CmscbbFreeWithNullCheck(eccValue);
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

    ret = InternalGetBigIntFromOcts(verifyInfo->e, &(eccValue->yValue));
    CmscbbFreeWithNullCheck(eccValue);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }
    return CVB_SUCCESS;
}

CVB_STATIC CMSCBB_ERROR_CODE InternalGetRsaPubKey(const CmscbbX509PublicKey* pubKey, CmscbbKeyAndAlgInfo* verifyInfo)
{
    CMSCBB_ERROR_CODE ret;
    const CmscbbRsaPublicKey *rsaPubKey = CVB_NULL;

    rsaPubKey = &pubKey->rsaPubKey;
    ret = InternalGetBigIntFromOcts(verifyInfo->e, &(rsaPubKey->publicExponent));
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

    ret = InternalGetBigIntFromOcts(verifyInfo->n, &(rsaPubKey->modules));
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }
    return ret;
}

CVB_STATIC CMSCBB_ERROR_CODE InternalGetEcdsaPubKey(const CmscbbAsnBits* pubKey, CmscbbKeyAndAlgInfo* verifyInfo)
{
    CMSCBB_ERROR_CODE ret;

    ret = InternalGetBigIntFromOcts(verifyInfo->eccKey, pubKey);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }
    return ret;
}

static CMSCBB_ERROR_CODE InternalX509ExtractPublicKey(const CmscbbX509PubkeyInfo* pubKey, CmscbbKeyAndAlgInfo* info)
{
    CMSCBB_ERROR_CODE ret;

    if (info->encAlg == AOID_ECDSA) {
        ret = InternalGetEcdsaPubKey(&pubKey->eccPubKey, info);
    } else {
        ret = InternalGetRsaPubKey(&pubKey->subjectPublicKey, info);
    }
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
    }
    return ret;
}

CMSCBB_ERROR_CODE CmscbbGetCmsVerifyInfo(const CmscbbPkcs7SignedInfo* si, const CmscbbX509Cert* cert,
    CmscbbKeyAndAlgInfo* verifyInfo)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbAoids hashAlgo;
    CmscbbAoids encAlgo;

    encAlgo = CmscbbGetEncAoidFromSign(&(si->signatureAlgo.algorithm));
    CVB_GOTO_ERR_WITH_LOG_IF(encAlgo == AOID_UNKNOWN, CMSCBB_ERR_PKI_CMS_HASH_ALGO);
    ret = CmscbbVerifyInfoInit(&(cert->toBeSigned.subjectPubKey), encAlgo, &(si->signatureAlgo), verifyInfo);
    CVB_GOTO_ERR_IF_FAIL(ret);

    hashAlgo = CmscbbGetHashAoidFromSign(&(si->digestAlgo.algorithm));
    CVB_GOTO_ERR_IF((hashAlgo == AOID_UNKNOWN), CMSCBB_ERR_PKI_CMS_HASH_ALGO);
    ret = InternalX509ExtractPublicKey(&cert->toBeSigned.subjectPubKey, verifyInfo);
    CVB_GOTO_ERR_IF_FAIL(ret);
    if (encAlgo == AOID_ECDSA) {
        ret = InternalGetEccSignature(&si->encryptedDigest, verifyInfo);
        if (CVB_FAILED(ret)) {
            CVB_LOG_DEBUG(ret, "Get ecc Signature Failed!");
            return ret;
        }
    }

    verifyInfo->hashAlg = hashAlgo;
CVB_ERR:
    return ret;
}

CMSCBB_ERROR_CODE CmscbbGetCertVerifyInfo(const CmscbbX509PubkeyInfo* pubKey,
    const CmscbbX509AlgoPssIdentifier* alg, const CmscbbAsnOcts* sigInfo, CmscbbKeyAndAlgInfo* verifyInfo)
{
    CMSCBB_ERROR_CODE ret;
    CmscbbAoids hashAlgo;
    CmscbbAoids encTempAlgo;

    encTempAlgo = CmscbbGetEncAoidFromSign(&(alg->algorithm));
    if (encTempAlgo == AOID_UNKNOWN) {
        CVB_LOG_ERROR(CMSCBB_ERR_PKI_CMS_HASH_ALGO, CVB_NULL);
        return (CMSCBB_ERR_PKI_CMS_HASH_ALGO);
    }
    ret = CmscbbVerifyInfoInit(pubKey, encTempAlgo, alg, verifyInfo);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }
    if (encTempAlgo == AOID_RSASSAPSS) {
        hashAlgo = CmscbbGetHashAoidFromSign(&(alg->pssPara.hashAlgorithm.algorithm));
    } else {
        hashAlgo = CmscbbGetHashAoidFromSign(&(alg->algorithm));
    }
    if (hashAlgo == AOID_UNKNOWN) {
        CVB_LOG_ERROR(CMSCBB_ERR_PKI_CMS_HASH_ALGO, CVB_NULL);
        return (CMSCBB_ERR_PKI_CMS_HASH_ALGO);
    }
    ret = InternalX509ExtractPublicKey(pubKey, verifyInfo);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }
    if (encTempAlgo == AOID_ECDSA) {
        ret = InternalGetEccSignature(sigInfo, verifyInfo);
        if (CVB_FAILED(ret)) {
            CVB_LOG_DEBUG(ret, "Get ecc Signature Failed!");
            return ret;
        }
    }

    verifyInfo->hashAlg = hashAlgo;
    return ret;
}


CVB_VOID CmscbbDestoryVerifyInfo(CmscbbKeyAndAlgInfo *verifyInfo)
{
    if (verifyInfo == CVB_NULL) {
        return;
    }
    CmscbbFreeWithNullCheck(verifyInfo->n);
    CmscbbFreeWithNullCheck(verifyInfo);
}
